package inutan.robot;

import bright.serialization.ByteBuf;
import inutan.common.constant.Constant;
import inutan.common.utils.HttpUtils;
import inutan.common.utils.RSAUtils;
import inutan.robot.broker.GameCodec;
import inutan.robot.broker.external.KcpExternalProtocol;
import inutan.robot.handler.RobotHandler;
import luban.common.Command;
import luban.user.LoginRequest;
import org.ricks.boot.Ricks;
import org.ricks.common.json.JSONObject;
import org.ricks.common.utils.RandomUtils;
import org.ricks.common.utils.ThreadUtils;
import org.ricks.common.utils.TimeUtils;
import org.ricks.net.*;
import org.ricks.net.buffer.BufferPagePool;
import org.ricks.net.http.Decoder;
import org.ricks.net.transport.Worker;
import org.ricks.net.transport.kcp.Kcp;
import org.ricks.net.transport.kcp.KcpBootstrap;
import org.ricks.net.transport.kcp.KcpChannel;
import org.ricks.net.transport.kcp.KcpSession;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.util.Base64;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author chenwei
 * @Description:网关节点
 * 对外 和 对内 消息转发
 * 维护外部连接 和 内部连接
 * 网关即使client 入口，那就意味网络维护了所有指令
 * 然后根据指令分配不同的业务集群 以及 集群节点
 *
 * 业务 to 业务，实际还是经过网关，然后再根据指令转发给 交互服 或者 逻辑服
 *
 * 网关维护所有的指令，根本指令做路由转发
 *
 * 简单来吧，注册中心就用redis ，网关节点启动 。网关节点的 IP 端口 注册进redis 中，其它业务节点启动启动直接从redis 获取所有网关信息 ，进行连接
 *
 * 全局统一编码 解码
 *
 * 1.消息转发，不需要堆业务代码 直接自定义handler 约定cmd职责范围进行不同模块集群转发
 * 2.维护玩家session ，以及业务节点session
 * 3.检验登陆权限 以及访问权限
 * 4.kcp & tcp 同时使用，收到什么协议请求就以什么协议返回，就得做下处理
 * 5.luban协议整合进网关
 *
 * 2023年2月16日 15:46:33
 * 1.登录 重新登录  顶号 处理  ，使用内部字节流解析器进行测试
 * 2.玩家退出 缓存clear 异步db数据 优化 测试
 * 3、外部协议luban 整合进broker中，&测试&客户端调试  【完成】
 * 4、玩家连接断线重连测试
 * 5、聊天搭建
 * 6、match server 搭建 以及 mmo 逻辑开发 收到mmo 消息缓存下来，然后以固定频率 广播房间其它成员 [50ms ? 广播频率待确认]
 * 7.策划表 生成data 机制调整
 * 8.class data 热更优化测试
 *
 * 内部协议基本完成制定
 */
public class RobotRun {

    private static Map<Integer,Integer> packId = new ConcurrentHashMap<>(1000);

    private static AtomicLong atomicLong = new AtomicLong();
    private static ScheduledExecutorService executorService = Executors.newScheduledThreadPool(32);

    private static Map<Integer, KcpSession> kcpSessionMap = new ConcurrentHashMap<>();

    public static void main(String[] args) throws InterruptedException, IOException {
        BufferPagePool bufferPagePool = new BufferPagePool(1024 * 1024 * 16, Runtime.getRuntime().availableProcessors(), true);

        Worker worker = new Worker(bufferPagePool, Runtime.getRuntime().availableProcessors());

        Ricks.run(RobotBootstrap.class);//启动容器 & 启动server
        DataCodecKit.setDataCodec(new GameCodec());
        KcpExternalProtocol protocol = new KcpExternalProtocol();
        RobotHandler handler = new RobotHandler();
        InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8888);

//        kcpBootstrap.setThreadNum(17);
//        for (int i = 0; i < 1; i++) {
//            KcpSession session = channel.createAndCacheSession(socketAddress,i);
//            login(session);
//        }

//        login(session);
//        loginHttpServer(session,"test-42");
//        System.err.println("..................................................机器人结束了");

        for (int i = 1; i <= 1; i++) {
            int finalI = i;
            new Thread(() -> {
                try {
                    KcpBootstrap bootstrap = new KcpBootstrap(protocol,handler,worker);

                    bootstrap.setBannerEnabled(false)
                            .setThreadNum(Runtime.getRuntime().availableProcessors())
                            .setBufferPagePool(bufferPagePool)
                            .setReadBufferSize(1024 * 2048);
                    KcpChannel channel = (KcpChannel) bootstrap.open();
                    KcpSession session = channel.createAndCacheSession(socketAddress, finalI);


                    int headLength = 2 + 4 + 2000;
                    //游戏默认是小端
                    ByteBuffer byteBuffer = ByteBuffer.allocate(headLength);
                    byteBuffer.putShort((short) 10001);
                    byteBuffer.putInt(1);
                    byte[] bytes  = RandomUtils.randomBytes(2000);
                    byteBuffer.put(bytes);

                    byteBuffer.flip();
                    byte[] data = new byte[headLength];
                    byteBuffer.get(data);

                    for (int i1 = 0; i1 < 1; i1++) {
                        session.send(data);
                        Thread.sleep(100);
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
//            int finalI = i;
//            KcpChannel channel = (KcpChannel) bootstrap.open();
//            KcpSession session = channel.createAndCacheSession(socketAddress, finalI);
//            kcpSessionMap.put(finalI, session);
//
//            ByteBuf data = new ByteBuf();
//
//            int headLength = 2 + 4 + 0;
//            //游戏默认是小端
//            ByteBuffer byteBuffer = ByteBuffer.allocate(headLength);
//            byteBuffer.putShort((short) 10001);
//            byteBuffer.putInt(1);
//            byteBuffer.put(new byte[0]);
//
//            byteBuffer.flip();
//            byte[] bytes = new byte[headLength];
//            byteBuffer.get(bytes);
//
//            Runnable runnable = () ->   session.send(bytes);;
//            executorService.scheduleAtFixedRate(runnable, 500L, 100l, TimeUnit.MILLISECONDS);
            ThreadUtils.sleep(10L);
        }


    }


    public static void writeAndFlush(KcpSession kcpSession,int packId) {
        int headLength = 2 + 4 + 2;
        //游戏默认是小端
        ByteBuffer byteBuffer = ByteBuffer.allocate(headLength);
        byteBuffer.putShort((short) 11111);
        byteBuffer.putInt(packId);
        byteBuffer.putShort((short) kcpSession.getUkcp().getConv());
        byteBuffer.put(new byte[0]);
        byteBuffer.flip();//重置 limit 和postion 值 否则 buffer 读取数据不对
        byte[] bytes = new byte[headLength];
        byteBuffer.get(bytes);

        kcpSession.send(bytes);
////            System.err.println("发送 字节长度：" + bytes.length + " & " + bytes[0] + "," + bytes[1]);
//        Kcp kcp = kcpSession.getUkcp();
//        int code;
//        try {
//            code = kcp.send(bytes);
//        } catch (Exception e) {
//            code = -9;
//        }
//        if (code == 0) kcp.update(TimeUtils.currentTimeMillis()); else kcpSession.close(true);
//            if (code != 0) kcpSession.close(true);
    }


    private static void loginHttpServer(KcpSession session, String username) {
        String loginInfo = HttpUtils.doGet("http://127.0.0.1:8080/login?openId=" + username);
        JSONObject jsonObject = new JSONObject(loginInfo);
        if(jsonObject.getInt("status") == 0) {
            long userToken = jsonObject.getLong("userToken");
            long loginTime = jsonObject.getLong("loginTime");

            StringBuilder builder = new StringBuilder(username);
            builder.append("&");
            builder.append(userToken);
            builder.append("&");
            builder.append(0);
            builder.append("&");
            builder.append(loginTime);
            try {
                String sign = RSAUtils.encryptByPublicKey(builder.toString(),RSAUtils.getPublicKey(Constant.PUBLIC_KEY));
                String a = RSAUtils.decryptByPrivateKey(sign, RSAUtils.getPrivateKey(Constant.PRIVATE_KEY));
                System.err.println("需要加密的字符串：" + builder +" &  公钥加密后字符串："+ sign + " & 私钥解密后的字符串：" + a);
                login(session, userToken, sign);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }


    public static void robotTest(KcpSession session) {

//        session.send(bytes);
    }


    private static void login(KcpSession session,long userToken, String sign) {


        ByteBuf data = new ByteBuf();
        LoginRequest loginRequest = new LoginRequest();
        loginRequest.sign = sign;
        loginRequest.userToken = userToken;
        loginRequest.serialize(data);
        byte[] arr = data.array();

        int headLength = 2 + 4 + arr.length;
        //游戏默认是小端
        ByteBuffer byteBuffer = ByteBuffer.allocate(headLength);
        byteBuffer.putShort(Command.Login);
        byteBuffer.putInt(1);
        byteBuffer.put(arr);

        byteBuffer.flip();
        byte[] bytes = new byte[headLength];
        byteBuffer.get(bytes);

        session.send(bytes);
    }
}
